using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text;
using DimensionsHelper.Common.Contracts;
using DimensionsHelper.Common.Contracts.TableFormatter;
using DimensionsHelper.Common.Contracts.TableFormatter.Options;
using DimensionsHelper.Common.Contracts.Excel;
using DimensionsHelper.Common.Utils;
using ExcelHAlign = DimensionsHelper.Common.Contracts.Excel.ExcelHAlign;
using ExcelVAlign = DimensionsHelper.Common.Contracts.Excel.ExcelVAlign;

namespace DimensionsHelper.Client.Helpers;

internal static class EnumStringCache
{
    private static readonly ConcurrentDictionary<Type, EnumCache> CacheMap = [];

    static EnumStringCache()
    {
        // ReportLevel
        ReportLevels = GetCache(typeof(ReportLevel)).Descriptions;
        // BorderStyle
        BorderStyleNames = GetCache(typeof(BorderStyle)).Descriptions;
        // HorizontalAlignment
        HorizontalAlignmentNames = GetCache(typeof(ExcelHAlign)).Descriptions;
        // VerticalAlignment
        VerticalAlignmentNames = GetCache(typeof(ExcelVAlign)).Descriptions;
        // UnderlineType
        UnderlineTypeNames = GetCache(typeof(UnderLineType)).Descriptions;
        // MarkType
        SigComparisonMarkTypeNames = GetCache(typeof(SigComparisonMarkType)).Descriptions;
        // SigGapType
        SigGapBoundaryTypeNames = GetCache(typeof(SigGapBoundaryType)).Descriptions;
        // MenuType
        MenuTypeNames = GetCache(typeof(MenuType)).Descriptions;
    }


    public static string FlagSeparator { get; set; } = "+";

    public static string[] ReportLevels { get; }

    public static string[] BorderStyleNames { get; }
    
    public static string[] HorizontalAlignmentNames { get; }
            
    public static string[] VerticalAlignmentNames { get; }
    
    public static string[] UnderlineTypeNames { get; }

    public static string[] SigComparisonMarkTypeNames { get; }
    
    public static string[] SigGapBoundaryTypeNames { get; }
    
    public static string[] MenuTypeNames { get; }


    private static EnumCache GetCache(Type enumType)
    {
        return CacheMap.GetOrAdd(enumType, type => new EnumCache(type));
    }


    public static string? GetIncompatibleDescription(Type enumType)
    {
        return GetCache(enumType).IncompatibleDescription;
    }


    public static IReadOnlyDictionary<string, T> GetEnumDescriptionMap<T>() where T : Enum
    {
        return GetCache(typeof(T)).AsReadOnlyDictionary<T>();
    }
    
    

    public static object GetEnumFromDescription(Type enumType, string description)
    {
        EnumCache cache = GetCache(enumType);
        return cache.GetEnum(description);
    }


    public static bool TryGetEnumFromDescription(Type enumType, string description,
        [MaybeNullWhen(false)] out object value)
    {
        value = null;
        EnumCache cache = GetCache(enumType);

        if (cache.IsEmpty)
        {
            return false;
        }

        value = cache.GetEnum(description);
        return true;
    }


    public static object GetEnumDefaultValue<T>()
    {
        return GetCache(typeof(T)).DefaultValue;
    }


    public static string GetEnumDefaultDescription<T>()
    {
        return GetCache(typeof(T)).Descriptions[0];
    }


    public static bool TryGetDescriptionFromEnum(object? value, [MaybeNullWhen(false)] out string description)
    {
        if (value == null)
        {
            description = null;
            return false;
        }

        Type enumType = value.GetType();
        EnumCache cache = GetCache(enumType);
        description = cache.GetDescription((int)value);
        return true;
    }


    private class EnumCache
    {
        // 保存 DescriptionAttribute 的描述和枚举值的对应关系。
        private readonly Dictionary<string, int> _descToEnumValue = [];
        private readonly Type _enumType;
        private readonly Dictionary<int, string> _enumValueToDesc = [];
        private readonly bool _isFlag;

        public EnumCache(Type enumType)
        {
            _enumType = enumType;
            // Flags
            _isFlag = enumType.GetCustomAttribute<FlagsAttribute>() != null;
            // 读取Description
            TypeHelper.ReadEnumMembers(enumType, info =>
            {
                DescriptionAttribute? attribute = info.Field.GetCustomAttribute<DescriptionAttribute>();

                if (string.IsNullOrEmpty(attribute?.Description))
                {
                    return;
                }
                // 如果带有 FlagsAttribute，将0值设置为互斥值描述
                if (_isFlag && info.Value == 0)
                {
                    IncompatibleDescription = attribute.Description;
                }
                
                _enumValueToDesc.TryAdd(info.Value, attribute.Description);
                _descToEnumValue.TryAdd(attribute.Description, info.Value);
            });
        }


        public bool IsEmpty => _enumValueToDesc.Count == 0;

        
        public object DefaultValue => Enum.ToObject(_enumType, _enumValueToDesc.Keys.First());


        public string[] Descriptions => _descToEnumValue.Keys.ToArray();
        
        
        public string? IncompatibleDescription { get; private set; }


        public IReadOnlyDictionary<string, T> AsReadOnlyDictionary<T>() where T : Enum
        {
            Dictionary<string, T> dictionary = [];
            
            foreach (KeyValuePair<string,int> pair in _descToEnumValue)
            {
                dictionary.Add(pair.Key, (T)Enum.ToObject(_enumType, pair.Value));
            }

            return dictionary;
        }
        

        public string GetDescription(int enumValue)
        {
            if (!_isFlag)
            {
                return _enumValueToDesc.GetValueOrDefault(enumValue, string.Empty);
            }

            StringBuilder fullDescription = new();

            foreach (KeyValuePair<int, string> pair in _enumValueToDesc)
            {
                if ((enumValue & pair.Key) == 0)
                {
                    continue;
                }

                if (fullDescription.Length > 0)
                {
                    fullDescription.Append(FlagSeparator);
                }

                fullDescription.Append(pair.Value);
            }

            return fullDescription.ToString();
        }


        public object GetEnum(string description)
        {
            if (Enum.TryParse(_enumType, description, true, out object? normalized))
            {
                return normalized;
            }
            
            if (!_isFlag)
            {
                int cachedValue = _descToEnumValue.TryGetValue(description, out int value)
                    ? value
                    : _descToEnumValue.Values.First();
                return Enum.ToObject(_enumType, cachedValue);
            }

            int fullValue = 0;

            foreach (string flagDescription in description.Split(FlagSeparator))
            {
                if (_descToEnumValue.TryGetValue(flagDescription, out int value))
                {
                    fullValue |= value;
                }
            }

            return Enum.ToObject(_enumType, fullValue);
        }
    }
}